#!/bin/bash

# K8s快速部署脚本
# 用于快速部署三节点K8s集群

set -e

# 配置文件路径
CONFIG_FILE="./k8s-config.conf"

# 加载配置文件
if [ -f "$CONFIG_FILE" ]; then
    source "$CONFIG_FILE"
    log "INFO" "配置文件加载成功"
else
    # 默认配置
    MASTER_IP="192.168.1.10"
    WORKER1_IP="192.168.1.11"
    WORKER2_IP="192.168.1.12"
    POD_NETWORK_CIDR="10.244.0.0/16"
    log "WARNING" "未找到配置文件，使用默认配置"
fi

# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'

log() {
    echo -e "${BLUE}[$(date '+%H:%M:%S')]${NC} $1"
}

success() {
    echo -e "${GREEN}[SUCCESS]${NC} $1"
}

warning() {
    echo -e "${YELLOW}[WARNING]${NC} $1"
}

error() {
    echo -e "${RED}[ERROR]${NC} $1"
    exit 1
}

# 检查是否为root用户
check_root() {
    if [ "$EUID" -ne 0 ]; then
        error "请使用root用户运行此脚本"
    fi
}

# 检查网络连通性
check_network() {
    log "检查网络连通性..."
    
    # 检查master节点
    if ping -c 1 $MASTER_IP &> /dev/null; then
        success "Master节点网络连通: $MASTER_IP"
    else
        error "无法连接到Master节点: $MASTER_IP"
    fi
    
    # 检查worker节点
    for worker in $WORKER1_IP $WORKER2_IP; do
        if ping -c 1 $worker &> /dev/null; then
            success "Worker节点网络连通: $worker"
        else
            warning "无法连接到Worker节点: $worker"
        fi
    done
}

# 准备服务器环境
prepare_server() {
    local node_type=$1
    local ip=$2
    
    log "准备服务器环境: $node_type ($ip)"
    
    # 检查准备脚本是否存在
    if [ ! -f "./prepare_server.sh" ]; then
        warning "未找到服务器准备脚本，跳过环境准备"
        return
    fi
    
    # 执行服务器准备
    chmod +x prepare_server.sh
    ./prepare_server.sh --hostname="$node_type" --ip="$ip"
    
    success "服务器环境准备完成"
}

# 配置主机名
configure_hostname() {
    local node_type=$1
    
    log "配置主机名: $node_type"
    
    # 检查主机名配置脚本是否存在
    if [ ! -f "./configure_hostnames.sh" ]; then
        warning "未找到主机名配置脚本，跳过主机名配置"
        return
    fi
    
    # 执行主机名配置
    chmod +x configure_hostnames.sh
    ./configure_hostnames.sh --node-type="$node_type"
    
    success "主机名配置完成"
}

# 配置网络
configure_network() {
    local node_type=$1
    local ip=$2
    
    log "配置网络: $node_type ($ip)"
    
    # 检查网络配置脚本是否存在
    if [ ! -f "./configure_network.sh" ]; then
        warning "未找到网络配置脚本，跳过网络配置"
        return
    fi
    
    # 执行网络配置
    chmod +x configure_network.sh
    ./configure_network.sh --node-type="$node_type" --ip="$ip"
    
    success "网络配置完成"
}

# 部署master节点
deploy_master() {
    log "开始部署Master节点..."
    
    # 准备服务器环境
    prepare_server "master" "$MASTER_IP"
    
    # 配置主机名
    configure_hostname "master"
    
    # 配置网络
    configure_network "master" "$MASTER_IP"
    
    # 检查安装包是否存在
    if [ ! -d "/root/k8s-offline-packages" ]; then
        error "未找到K8s安装包，请先运行下载脚本"
    fi
    
    cd /root/k8s-offline-packages
    
    # 安装master节点
    log "安装K8s组件..."
    ./install_k8s_offline.sh --node-type=master --master-ip=$MASTER_IP
    
    # 等待服务启动
    log "等待服务启动..."
    sleep 30
    
    # 检查安装状态
    log "检查安装状态..."
    ./check_k8s_health.sh
    
    # 获取join命令
    log "获取Worker节点加入命令..."
    kubeadm token create --print-join-command > /root/join-command.txt
    
    success "Master节点部署完成！"
    log "Join命令已保存到: /root/join-command.txt"
    cat /root/join-command.txt
}

# 部署worker节点
deploy_worker() {
    local worker_ip=$1
    local worker_name=$2
    
    log "开始部署Worker节点: $worker_name ($worker_ip)"
    
    # 准备服务器环境
    prepare_server "$worker_name" "$worker_ip"
    
    # 配置主机名
    configure_hostname "$worker_name"
    
    # 配置网络
    configure_network "$worker_name" "$worker_ip"
    
    # 检查安装包是否存在
    if [ ! -d "/root/k8s-offline-packages" ]; then
        error "未找到K8s安装包，请先运行下载脚本"
    fi
    
    cd /root/k8s-offline-packages
    
    # 从master节点获取join命令
    if [ ! -f "/root/join-command.txt" ]; then
        error "未找到join命令文件，请先在master节点运行部署"
    fi
    
    # 解析join命令
    local join_cmd=$(cat /root/join-command.txt)
    local token=$(echo $join_cmd | grep -o '--token [^ ]*' | cut -d' ' -f2)
    local cert_hash=$(echo $join_cmd | grep -o '--discovery-token-ca-cert-hash sha256:[^ ]*' | cut -d':' -f2)
    
    if [ -z "$token" ] || [ -z "$cert_hash" ]; then
        error "无法解析join命令"
    fi
    
    # 安装worker节点
    log "安装K8s组件..."
    ./install_k8s_offline.sh --node-type=worker --master-ip=$MASTER_IP --token=$token --cert-hash=$cert_hash
    
    # 等待服务启动
    log "等待服务启动..."
    sleep 30
    
    # 检查安装状态
    log "检查安装状态..."
    ./check_k8s_health.sh
    
    success "Worker节点 $worker_name 部署完成！"
}

# 安装网络插件
install_network_plugin() {
    log "安装网络插件..."
    
    # 创建Flannel配置
    cat > /tmp/flannel.yaml << 'EOF'
apiVersion: v1
kind: Namespace
metadata:
  name: kube-flannel
  labels:
    pod-security.kubernetes.io/enforce: privileged
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: flannel
  namespace: kube-flannel
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: flannel
rules:
- apiGroups:
  - ""
  resources:
  - pods
  verbs:
  - get
- apiGroups:
  - ""
  resources:
  - nodes
  verbs:
  - list
  - watch
- apiGroups:
  - ""
  resources:
  - nodes/status
  verbs:
  - patch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: flannel
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: flannel
subjects:
- kind: ServiceAccount
  name: flannel
  namespace: kube-flannel
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: kube-flannel-cfg
  namespace: kube-flannel
data:
  cni-conf.json: |
    {
      "name": "cbr0",
      "cniVersion": "0.3.1",
      "plugins": [
        {
          "type": "flannel",
          "delegate": {
            "hairpinMode": true,
            "isDefaultGateway": true
          }
        },
        {
          "type": "portmap",
          "capabilities": {
            "portMappings": true
          }
        }
      ]
    }
  net-conf.json: |
    {
      "Network": "10.244.0.0/16",
      "Backend": {
        "Type": "vxlan"
      }
    }
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: kube-flannel-ds
  namespace: kube-flannel
spec:
  selector:
    matchLabels:
      app: flannel
  template:
    metadata:
      labels:
        app: flannel
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: kubernetes.io/os
                operator: In
                values:
                - linux
      hostNetwork: true
      priorityClassName: system-node-critical
      tolerations:
      - operator: Exists
        effect: NoSchedule
      serviceAccountName: flannel
      initContainers:
      - name: install-cni-plugin
        image: docker.io/flannel/flannel-cni-plugin:v1.1.0
        command:
        - cp
        args:
        - -f
        - /flannel
        - /opt/cni/bin/flannel
        volumeMounts:
        - name: cni-plugin
          mountPath: /opt/cni/bin
      - name: install-cni
        image: docker.io/flannel/flannel:v0.22.0
        command:
        - cp
        args:
        - -f
        - /etc/kube-flannel/cni-conf.json
        - /etc/cni/net.d/10-flannel.conflist
        volumeMounts:
        - name: cni
          mountPath: /etc/cni/net.d
        - name: flannel-cfg
          mountPath: /etc/kube-flannel/
      containers:
      - name: kube-flannel
        image: docker.io/flannel/flannel:v0.22.0
        command:
        - /opt/bin/flanneld
        args:
        - --ip-masq
        - --kube-subnet-mgr
        resources:
          requests:
            cpu: "100m"
            memory: "50Mi"
          limits:
            cpu: "100m"
            memory: "50Mi"
        securityContext:
          privileged: false
          capabilities:
            add: ["NET_ADMIN", "NET_RAW"]
        env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: EVENT_QUEUE_DEPTH
          value: "5000"
        volumeMounts:
        - name: run
          mountPath: /run/flannel
        - name: flannel-cfg
          mountPath: /etc/kube-flannel/
        - name: xtables-lock
          mountPath: /run/xtables.lock
      volumes:
      - name: run
        hostPath:
          path: /run/flannel
      - name: cni-plugin
        hostPath:
          path: /opt/cni/bin
      - name: cni
        hostPath:
          path: /etc/cni/net.d
      - name: flannel-cfg
        configMap:
          name: kube-flannel-cfg
      - name: xtables-lock
        hostPath:
          path: /run/xtables.lock
          type: FileOrCreate
EOF
    
    # 应用Flannel配置
    kubectl apply -f /tmp/flannel.yaml
    
    # 等待网络插件就绪
    log "等待网络插件就绪..."
    kubectl wait --for=condition=ready pod -l app=flannel -n kube-flannel --timeout=300s
    
    success "网络插件安装完成！"
}

# 验证集群
verify_cluster() {
    log "验证集群状态..."
    
    # 检查节点状态
    log "检查节点状态..."
    kubectl get nodes -o wide
    
    # 检查系统Pod
    log "检查系统Pod..."
    kubectl get pods -n kube-system
    
    # 检查网络插件
    log "检查网络插件..."
    kubectl get pods -n kube-flannel
    
    # 部署测试应用
    log "部署测试应用..."
    kubectl create deployment nginx --image=nginx
    kubectl expose deployment nginx --port=80 --type=NodePort
    
    # 等待应用就绪
    kubectl wait --for=condition=available deployment/nginx --timeout=300s
    
    # 获取服务信息
    local node_port=$(kubectl get service nginx -o jsonpath='{.spec.ports[0].nodePort}')
    local node_ip=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[?(@.type=="InternalIP")].address}')
    
    success "集群验证完成！"
    log "测试应用访问地址: http://$node_ip:$node_port"
    
    # 清理测试应用
    kubectl delete deployment nginx
    kubectl delete service nginx
}

# 显示使用说明
show_usage() {
    echo "K8s快速部署脚本"
    echo ""
    echo "用法:"
    echo "  $0 master                    # 部署master节点"
    echo "  $0 worker <worker-ip>        # 部署worker节点"
    echo "  $0 network                   # 安装网络插件"
    echo "  $0 verify                    # 验证集群"
    echo "  $0 all                       # 完整部署流程"
    echo ""
    echo "示例:"
    echo "  $0 master                   # 在master节点运行"
    echo "  $0 worker 192.168.1.11      # 在worker节点运行"
    echo "  $0 network                  # 在master节点安装网络插件"
    echo "  $0 verify                   # 验证集群状态"
}

# 主函数
main() {
    local action=$1
    local worker_ip=$2
    
    check_root
    
    case $action in
        "master")
            check_network
            deploy_master
            ;;
        "worker")
            if [ -z "$worker_ip" ]; then
                error "请指定worker节点IP地址"
            fi
            check_network
            deploy_worker $worker_ip "worker-$(echo $worker_ip | cut -d'.' -f4)"
            ;;
        "network")
            install_network_plugin
            ;;
        "verify")
            verify_cluster
            ;;
        "all")
            log "开始完整部署流程..."
            check_network
            deploy_master
            log "请在其他节点运行: $0 worker <worker-ip>"
            log "然后在master节点运行: $0 network"
            log "最后运行: $0 verify"
            ;;
        *)
            show_usage
            exit 1
            ;;
    esac
}

# 运行主函数
main "$@"
